/*
 * txtquery 操作与 ltree
 * Teodor Sigaev <teodor@stack.net>
 * contrib/ltree/ltxtquery_op.c
 */
#include "postgres.h"

#include <ctype.h>

#include "ltree.h"
#include "miscadmin.h"

PG_FUNCTION_INFO_V1(ltxtq_exec);
PG_FUNCTION_INFO_V1(ltxtq_rexec);

/*
 * 检查布尔条件
 */
bool ltree_execute(ITEM *fc_curitem, void *fc_checkval, bool fc_calcnot, bool (*fc_chkcond) (void *checkval, ITEM *val))
{
	/* 由于这个函数递归，可能会导致栈溢出 */
	check_stack_depth();

	if (fc_curitem->type == VAL)
		return (*fc_chkcond) (fc_checkval, fc_curitem);
	else if (fc_curitem->val == (int32) '!')
	{
		return fc_calcnot ?
			((ltree_execute(fc_curitem + 1, fc_checkval, fc_calcnot, fc_chkcond)) ? false : true)
			: true;
	}
	else if (fc_curitem->val == (int32) '&')
	{
		if (ltree_execute(fc_curitem + fc_curitem->left, fc_checkval, fc_calcnot, fc_chkcond))
			return ltree_execute(fc_curitem + 1, fc_checkval, fc_calcnot, fc_chkcond);
		else
			return false;
	}
	else
	{							/* |-操作符 */
		if (ltree_execute(fc_curitem + fc_curitem->left, fc_checkval, fc_calcnot, fc_chkcond))
			return true;
		else
			return ltree_execute(fc_curitem + 1, fc_checkval, fc_calcnot, fc_chkcond);
	}
}

typedef struct
{
	ltree	   *node;
	char	   *operand;
} CHKVAL;

static bool fc_checkcondition_str(void *fc_checkval, ITEM *fc_val)
{
	ltree_level *fc_level = LTREE_FIRST(((CHKVAL *) fc_checkval)->node);
	int			fc_tlen = ((CHKVAL *) fc_checkval)->node->numlevel;
	char	   *fc_op = ((CHKVAL *) fc_checkval)->operand + fc_val->distance;
	int			(*fc_cmpptr) (const char *, const char *, size_t);

	fc_cmpptr = (fc_val->flag & LVAR_INCASE) ? ltree_strncasecmp : strncmp;
	while (fc_tlen > 0)
	{
		if (fc_val->flag & LVAR_SUBLEXEME)
		{
			if (compare_subnode(fc_level, fc_op, fc_val->length, fc_cmpptr, (fc_val->flag & LVAR_ANYEND)))
				return true;
		}
		else if ((fc_val->length == fc_level->len ||
				  (fc_level->len > fc_val->length && (fc_val->flag & LVAR_ANYEND))) &&
				 (*fc_cmpptr) (fc_op, fc_level->name, fc_val->length) == 0)
			return true;

		fc_tlen--;
		fc_level = LEVEL_NEXT(fc_level);
	}

	return false;
}

Datum ltxtq_exec(PG_FUNCTION_ARGS)
{
	ltree	   *fc_val = PG_GETARG_LTREE_P(0);
	ltxtquery  *fc_query = PG_GETARG_LTXTQUERY_P(1);
	CHKVAL		fc_chkval;
	bool		fc_result;

	fc_chkval.node = fc_val;
	fc_chkval.operand = GETOPERAND(fc_query);

	fc_result = ltree_execute(GETQUERY(fc_query),
						   &fc_chkval,
						   true,
						   fc_checkcondition_str);

	PG_FREE_IF_COPY(fc_val, 0);
	PG_FREE_IF_COPY(fc_query, 1);
	PG_RETURN_BOOL(fc_result);
}

Datum ltxtq_rexec(PG_FUNCTION_ARGS)
{
	PG_RETURN_DATUM(DirectFunctionCall2(ltxtq_exec,
										PG_GETARG_DATUM(1),
										PG_GETARG_DATUM(0)
										));
}
